package com.cnap.service.impl;

import cn.hutool.core.codec.Base64Encoder;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpStatus;
import com.cnap.config.DockerConfig;
import com.cnap.config.GatewayConfig;
import com.cnap.db.entity.ApplicationPO;
import com.cnap.db.entity.ClusterInfoPO;
import com.cnap.db.entity.DeployClusterPO;
import com.cnap.db.entity.MicroServicePO;
import com.cnap.db.entity.ProgressPO;
import com.cnap.db.entity.ScaleStrategyPO;
import com.cnap.db.entity.ScheduleStrategyPO;
import com.cnap.db.entity.StrategyPO;
import com.cnap.db.mapper.MicroServiceMapper;
import com.cnap.db.mapper.ScaleStrategyMapper;
import com.cnap.db.mapper.ScheduleStrategyMapper;
import com.cnap.db.mapper.StrategyMapper;
import com.cnap.model.application.ApplicationLabel;
import com.cnap.model.application.ApplicationStatusEnum;
import com.cnap.model.application.CategoryEnum;
import com.cnap.model.application.DeployResourceInfo;
import com.cnap.model.cloud.CloudVendorsEnum;
import com.cnap.model.cloud.CommentResultForCloud;
import com.cnap.model.cloud.DeleteLocalImageParam;
import com.cnap.model.cloud.scaling.CreateScalingDTO;
import com.cnap.model.cloud.scaling.ScalingPolicyDTO;
import com.cnap.model.cloud.scaling.ScalingPolicyPO;
import com.cnap.model.cloud.scaling.UpdateScalingParam;
import com.cnap.model.common.PageResult;
import com.cnap.model.common.ResultCode;
import com.cnap.model.exception.JsonException;
import com.cnap.model.image.ImageProcessTask;
import com.cnap.model.image.config.ConfigImageInfo;
import com.cnap.model.image.config.ConfigInfo;
import com.cnap.model.image.config.ConfigServiceInfo;
import com.cnap.model.image.config.ConfigYamlInfo;
import com.cnap.model.progress.ProgressConstants;
import com.cnap.model.schedule.ClusterInfoVO;
import com.cnap.model.schedule.DistributeParam;
import com.cnap.model.schedule.MicroServiceDTO;
import com.cnap.model.schedule.MicroServiceVO;
import com.cnap.model.schedule.QueryMicroServiceParam;
import com.cnap.model.schedule.ScaleStrategyTypeEnum;
import com.cnap.model.schedule.StrategyStatusEnum;
import com.cnap.model.schedule.StrategyTypeEnum;
import com.cnap.model.schedule.UpdateMicroServiceStatusParam;
import com.cnap.model.schedule.UpdateMicroserviceClusterParam;
import com.cnap.model.schedule.UpdateUserChooseParam;
import com.cnap.model.schedule.ValidateParam;
import com.cnap.model.schedule.engine.CommentResultForEngine;
import com.cnap.model.schedule.engine.DeployPolicyDistributeReq;
import com.cnap.model.schedule.engine.DeployPolicyValidateReq;
import com.cnap.model.schedule.engine.EngineClusterInfo;
import com.cnap.model.schedule.engine.ValidateResponse;
import com.cnap.model.schedule.engine.ValidateResult;
import com.cnap.model.stage.StageEnum2;
import com.cnap.model.stage.UpdateStageParam;
import com.cnap.service.AlgorithmEngineService;
import com.cnap.service.ApplicationService;
import com.cnap.service.CloudMgmtService;
import com.cnap.service.ClusterInfoService;
import com.cnap.service.DistributeDeployService;
import com.cnap.service.FileService;
import com.cnap.service.IndicatorService;
import com.cnap.service.MicroSvcService;
import com.cnap.service.ProgressService;
import com.cnap.service.StrategyService;
import com.cnap.service.UserSysService;
import com.cnap.utils.CommonUtils;
import com.cnap.utils.JsonUtil;
import com.cnap.utils.PageUtils;
import com.cnap.utils.TokenUtils;
import com.github.pagehelper.PageInfo;
import com.github.pagehelper.page.PageMethod;
import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.EnvVar;
import io.fabric8.kubernetes.api.model.GenericKubernetesResource;
import io.fabric8.kubernetes.api.model.KubernetesResource;
import io.fabric8.kubernetes.api.model.Secret;
import io.fabric8.kubernetes.api.model.ServiceAccount;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.api.model.rbac.Role;
import io.fabric8.kubernetes.api.model.rbac.RoleBinding;
import io.fabric8.kubernetes.client.utils.Serialization;
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.progress.ProgressMonitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

import static com.cnap.service.impl.OperateServiceImpl.ASYNC_DELETE_MAP;

/**
 * <类文件描述>
 *
 * @author xc
 **/
@Service
public class MicroSvcServiceImpl implements MicroSvcService {
    private static final Logger LOGGER = LoggerFactory.getLogger(MicroSvcServiceImpl.class);

    private static final ExecutorService EXECUTOR_SERVICE = ThreadUtil.newExecutor(5, 10, 50);

    private final MicroServiceMapper microServiceMapper;
    private final StrategyMapper strategyMapper;
    private final ApplicationService applicationService;
    private final FileService fileService;
    private final ProgressService progressService;
    private final DockerConfig dockerConfig;
    private final AlgorithmEngineService algorithmEngineService;
    private final GatewayConfig gatewayConfig;
    private final ClusterInfoService clusterInfoService;
    private final CloudMgmtService cloudMgmtService;
    private final IndicatorService indicatorService;
    private final ScaleStrategyMapper scaleStrategyMapper;
    private final ScheduleStrategyMapper scheduleStrategyMapper;
    private final UserSysService userSysService;

    @Autowired
    public MicroSvcServiceImpl(MicroServiceMapper microServiceMapper, StrategyMapper strategyMapper,
                               ApplicationService applicationService,
                               FileService fileService, ProgressService progressService, DockerConfig dockerConfig,
                               AlgorithmEngineService algorithmEngineService, GatewayConfig gatewayConfig,
                               ClusterInfoService clusterInfoService,
                               CloudMgmtService cloudMgmtService, IndicatorService indicatorService,
                               ScaleStrategyMapper scaleStrategyMapper, ScheduleStrategyMapper scheduleStrategyMapper,
                               UserSysService userSysService) {
        this.microServiceMapper = microServiceMapper;
        this.strategyMapper = strategyMapper;
        this.applicationService = applicationService;
        this.fileService = fileService;
        this.progressService = progressService;
        this.dockerConfig = dockerConfig;
        this.algorithmEngineService = algorithmEngineService;
        this.gatewayConfig = gatewayConfig;
        this.clusterInfoService = clusterInfoService;
        this.cloudMgmtService = cloudMgmtService;
        this.indicatorService = indicatorService;
        this.scaleStrategyMapper = scaleStrategyMapper;
        this.scheduleStrategyMapper = scheduleStrategyMapper;
        this.userSysService = userSysService;
    }

    /**
     * 分解微服务
     *
     * @param applicationId 应用id
     */
    @Override
    public void splitMicroServices(String applicationId) {
        // 判断是否存在应用
        ApplicationPO applicationPO = applicationService.selectByAppId(applicationId);
        if (Objects.isNull(applicationPO)) {
            LOGGER.error("can't get application by id={}", applicationId);
            throw new JsonException(500, "未找到该应用");
        }
        // 由于是内部调用，此处可以不用判断权限了

        // 重复分解微服务，直接返回
        List<MicroServicePO> microServicePOS = microServiceMapper.selectMicroServiceByAppId(applicationId);
        // 根据微服务数量判断
        if (Objects.nonNull(microServicePOS) && !microServicePOS.isEmpty()) {
            LOGGER.info("application id={} has split micro services, set progress 100 and return", applicationId);

            return;
        }
        ProgressPO existProgress = progressService.getProgressPOByApplicationId(applicationId);
        // 判断进度条
        if (ProgressConstants.TYPE_SPLIT.equals(existProgress.getType())) {
            LOGGER.info("application id={} has split micro services, but haven't save micro services into db, just " +
                            "return and wait",
                    applicationId);
            return;
        }

        String savePath = fileService.getSavePath(applicationId);
        String unzipPath = savePath + File.separator + "unzip";

        String zipFilePath = savePath + File.separator + applicationId + "." + "zip";
        // 判断压缩文件
        if (!FileUtil.exist(zipFilePath)) {
            LOGGER.error("zip file path is invalid, path={}", zipFilePath);
            throw new JsonException(500, "未找到压缩文件");
        }
        // 更新进度条
        ProgressPO progressPO = new ProgressPO();
        progressPO.setId(applicationId);
        progressPO.setPercent(0);
        progressPO.setStage(ProgressConstants.STAGE_SPLIT_UNZIP);
        progressPO.setType(ProgressConstants.TYPE_SPLIT);
        progressService.updateProgress(progressPO);

        // 解压缩
        CompletableFuture<Void> unzipFuture = CompletableFuture.runAsync(() ->
                unzipFile(applicationId, zipFilePath, unzipPath)
        );
        // 4. 处理镜像
        processImage(applicationId, unzipFuture, unzipPath);


    }

    private void unzipFile(String applicationId, String zipFilePath, String unzipPath) {
        // 解压
        try (ZipFile zipFile = new ZipFile(zipFilePath)) {
            ProgressMonitor monitor = zipFile.getProgressMonitor();
            zipFile.setRunInThread(true);
            zipFile.extractAll(unzipPath);

            while (!monitor.getState().equals(ProgressMonitor.State.READY)) {
                int innerUnzipPercent = monitor.getPercentDone();
                int currentPercent = (ProgressConstants.UPLOAD_MERGE_PERCENT * innerUnzipPercent) / 100;
                progressService.updateProgressPercent(applicationId, currentPercent);
                // 1秒写入一次
                ThreadUtil.safeSleep(500);
            }
        } catch (IOException e) {
            LOGGER.error("unzip file error, file={}, error={}", applicationId, e.getMessage());
            progressService.updateProgressException(applicationId, "解压文件错误，请检查");
        }

    }

    private void processImage(String applicationId, CompletableFuture<Void> unzipFuture, String unzipPath) {
        unzipFuture.thenRunAsync(() -> {
            // 设置为进度条 分解微服务，推送镜像状态
            ProgressPO progressPO = new ProgressPO();
            progressPO.setId(applicationId);
            progressPO.setPercent(ProgressConstants.SPLIT_UNZIP_PERCENT);
            progressPO.setStage(ProgressConstants.STAGE_SPLIT_PUSH);
            progressPO.setType(ProgressConstants.TYPE_SPLIT);
            progressService.updateProgress(progressPO);

            File unzipFileFolder = new File(unzipPath);
            String[] unzipFiles = unzipFileFolder.list();
            if (Objects.isNull(unzipFiles) || unzipFiles.length == 0) {
                LOGGER.error("zip file is empty, parentId={}", applicationId);
                progressService.updateProgressException(applicationId, "文件异常，请检查");
                return;
            }
            // 多线程处理
            List<CompletableFuture<Boolean>> pushImageFutures = new ArrayList<>(unzipFiles.length);
            int size = unzipFiles.length;
            // 最终结果标志
            AtomicBoolean flag = new AtomicBoolean(true);
            // 每个镜像占比
            int percent = ProgressConstants.SPLIT_PUSH_PERCENT / size;
            // 处理单个文件
            processEachFile(applicationId, unzipPath, unzipFiles, pushImageFutures, flag, percent);
            // 结束
            // 推送镜像需要返回值，表明推送成功与否
            CompletableFuture.allOf(pushImageFutures.toArray(new CompletableFuture[0])).thenRun(() -> {
                        if (flag.get()) {
                            // 都结束后进度条置为100
                            progressService.updateProgressPercent(applicationId, 100);
                            applicationService.updateApplicationStatus(applicationId,
                                    ApplicationStatusEnum.SPLIT.getValue());
                            // TODO 更新配额
                            ApplicationPO applicationPO = applicationService.selectByAppId(applicationId);
                            userSysService.updateQuota(1, applicationPO.getUserName(), "apply");
                        } else {
                            // 异常情况，删除本地文件
                            String basePath = fileService.getSavePath(applicationId);
                            if (FileUtil.exist(basePath)) {
                                LOGGER.error("zip file catch error, try to delete all patched and unzip files, id={}"
                                        , applicationId);
                                FileUtil.del(basePath);
                            }
                            // 删除微服务信息
                            microServiceMapper.deleteMicroServiceByAppId(applicationId);
                            // 删除指标实例信息
                            indicatorService.deleteIndicatorCaseByAppId(applicationId);
                        }

                    }
            ).join();
        });
    }

    private void processEachFile(String applicationId, String unzipPath, String[] unzipFiles,
                                 List<CompletableFuture<Boolean>> pushImageFutures, AtomicBoolean flag, int percent) {
        for (String unzipFile : unzipFiles) {
            // 所有信息均在配置文件中写明
            String basePath = unzipPath + File.separator + unzipFile;
            String configPath = basePath + File.separator + "config";
            Properties properties = new Properties();
            // 读取文件
            try (FileInputStream inputStream = new FileInputStream(configPath);
                 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
            ) {
                properties.load(bufferedReader);
                if (!checkConfigFile(properties)) {
                    LOGGER.error("check file fail, {}", unzipFile);
                    progressService.updateProgressException(applicationId, "配置文件校验失败，请检查");
                    flag.set(false);
                    return;
                }
                ConfigInfo configInfo = getConfigFromFile(properties);

                // 1. 校验哈希值
                String imageTarPath = basePath + File.separator + configInfo.getConfigImageInfo().getImage();
                if (!FileUtil.exist(imageTarPath)) {
                    LOGGER.error("can't find image tar, current file={}", unzipFile);
                    progressService.updateProgressException(applicationId, "没有找到镜像文件");
                    flag.set(false);
                    return;
                }
                // 镜像文件
                File imageFile = new File(imageTarPath);
                String imageTarMd5 = SecureUtil.md5(imageFile);
                // 校验镜像MD5
                if (!imageTarMd5.equals(configInfo.getConfigImageInfo().getImageMd5())) {
                    LOGGER.error("current file md5={} is invalid width config file md5={}", imageTarMd5, configInfo.getConfigImageInfo().getImageMd5());
                    progressService.updateProgressException(applicationId, configInfo.getConfigImageInfo().getImage() +
                            "哈希值与配置文件中的哈希值不一致，请检查");
                    flag.set(false);
                    return;
                }

                LOGGER.info("config file for {}: {}", unzipFile, configInfo);
                ApplicationPO applicationPO = applicationService.selectByAppId(applicationId);

                CompletableFuture<Boolean> future = futureProcess(applicationId, applicationPO.getUserName(), flag, percent,
                        unzipFile,
                        configInfo, imageFile);
                pushImageFutures.add(future);
            } catch (IOException e) {
                LOGGER.error("can't read config file, current file={}, exception={}", unzipFile, e.getMessage());
                progressService.updateProgressException(applicationId, "压缩文件内没有找到配置文件");
                flag.set(false);
            }
        }
    }

    private boolean checkConfigFile(Properties properties) {
        // image
        if (CharSequenceUtil.isBlank(properties.getProperty("image"))) {
            return false;
        }
        // imageMd5
        if (CharSequenceUtil.isBlank(properties.getProperty("imageMd5"))) {
            return false;
        }
        // imageName
        if (CharSequenceUtil.isBlank(properties.getProperty("imageName"))) {
            return false;
        }
        // yaml
        if (CharSequenceUtil.isBlank(properties.getProperty("yaml"))) {
            return false;
        }
        // serviceName
        if (CharSequenceUtil.isBlank(properties.getProperty("serviceName"))) {
            return false;
        }
        // framework
        if (CharSequenceUtil.isBlank(properties.getProperty("framework"))) {
            return false;
        }
        // path
        String pathStr = properties.getProperty("path");
        if (CharSequenceUtil.isBlank(pathStr)) {
            return false;
        }
        // 多路径使用‘,’分割
        List<String> path = Arrays.stream(pathStr.split(",")).map(String::trim).collect(Collectors.toList());
        return !path.isEmpty();
    }

    private CompletableFuture<Boolean> futureProcess(String applicationId, String userName, AtomicBoolean flag,
                                                     int percent,
                                                     String unzipFile, ConfigInfo configInfo, File imageFile) {
        ImageProcessTask imageProcessTask = new ImageProcessTask(unzipFile, applicationId
                , progressService, dockerConfig, configInfo, imageFile, userName);
        // yaml完整路径
        String yamlPath = imageFile.getParent() + File.separator + configInfo.getConfigYamlInfo().getYamlName();

        return CompletableFuture.supplyAsync(() -> imageProcessTask, EXECUTOR_SERVICE).handle((res, exp) -> {
            // 镜像处理出错
            if (Objects.nonNull(exp)) {
                LOGGER.error("image process catch error: {}", exp.getMessage());
                progressService.updateProgressException(applicationId, "镜像处理出错");
                return false;
            }
            boolean result = res.getAsBoolean();
            flag.set(result && flag.get());
            if (result) {
                int currentPercent = progressService.getPercentByApplicationId(applicationId);
                progressService.updateProgressPercent(applicationId, currentPercent + percent);
                // 插入微服务信息
                MicroServicePO po = saveMicroService(applicationId, configInfo, yamlPath, imageFile.length());
                ApplicationPO appPO = applicationService.selectByAppId(applicationId);
                // 插入指标实例信息
                // 指标实例化，移至添加调度策略处

                return true;
            } else {
                return false;
            }
        });
    }

    public MicroServicePO saveMicroService(String applicationId, ConfigInfo configInfo, String yamlFilePath,
                                           long imageFileLength) {
        ApplicationPO applicationPO = applicationService.selectByAppId(applicationId);
        // 镜像带有本地harbor前缀
        // yaml里需要更改镜像地址
        MicroServicePO microServicePO = new MicroServicePO();
        microServicePO.setId(IdUtil.simpleUUID());
        microServicePO.setMicroServiceName(configInfo.getConfigServiceInfo().getServiceName());
        microServicePO.setFramework(configInfo.getConfigServiceInfo().getFramework());

        microServicePO.setApplicationId(applicationId);
        String newImageName = getImageNameWithHarborPrefix(configInfo.getConfigImageInfo().getImageName(),
                applicationPO.getUserName());
        // 需要从application处获取username和appName
        String nameSpace = CommonUtils.getNamespaceByUserNameAndAppName(applicationPO.getUserName(),
                applicationPO.getName());

        // 用list作为引用传递，在方法中加入deploymentName，取巧的办法
        // 具体参考值传递与引用传递
        List<String> deploymentNames = new ArrayList<>(1);
        List<String> serviceNames = new ArrayList<>(1);

        try (FileInputStream inputStream = new FileInputStream(yamlFilePath)) {
            // yaml里面的k8s资源
            List<KubernetesResource> kubernetesResources = getKubernetesResources(newImageName, inputStream,
                    nameSpace, deploymentNames, serviceNames);
            // Serialization.asYaml() 只能生成单个资源的yaml
            String yaml = kubernetesResources.stream().map(Serialization::asYaml).collect(Collectors.joining());

            LOGGER.debug("save new yaml: {}", yaml);
            microServicePO.setYaml(yaml);
            microServicePO.setStatus(StrategyStatusEnum.INIT.getStatus());
            microServicePO.setImageName(newImageName);
            microServicePO.setPath(getPathWithPrefix(configInfo.getConfigServiceInfo().getPath(), applicationId,
                    microServicePO.getId()));
            microServicePO.setSize(CommonUtils.getPrintSize(imageFileLength / 1024));
            microServicePO.setNamespace(nameSpace);
            microServicePO.setDeploymentName(deploymentNames.get(0));
            microServicePO.setServiceName(serviceNames.get(0));

            // 保存微服务
            microServiceMapper.insertMicroService(microServicePO);
        } catch (IOException e) {
            LOGGER.error("can't find yaml file, file={}", yamlFilePath);
            progressService.updateProgressException(applicationId, "没有找到镜像文件");
        }
        return microServicePO;
    }

    /**
     * 获取带有harbor前缀的镜像全名，带tag
     *
     * @param imageFullName 镜像原来的全名，带tag
     * @return 镜像全名
     */
    private String getImageNameWithHarborPrefix(String imageFullName, String userName) {
        // imageName
        String imageName = imageFullName.substring(imageFullName.lastIndexOf("/") + 1, imageFullName.indexOf(":"));
        // tag
        String tag = imageFullName.substring(imageFullName.lastIndexOf(":") + 1);
        // 全路径
        String md5 = SecureUtil.md5(userName);
        // 使用用户名哈希过后的前8位
        return dockerConfig.getRegistryPrefix() + md5.substring(0, 8) + "/" + imageName + ":" + tag;
    }

    /**
     * 获取处理过后的yaml
     *
     * @param newImageName 新镜像名称
     * @param inputStream  yaml文件
     * @return 结果
     */
    private List<KubernetesResource> getKubernetesResources(String newImageName, FileInputStream inputStream,
                                                            String nameSpace, List<String> deploymentNames,
                                                            List<String> serviceNames) {
        // 读取所有k8s资源
        List<KubernetesResource> kubernetesResources = Serialization.unmarshal(inputStream,
                Collections.emptyMap());
        // 循环处理
        // 主要设计ns，环境变量
        for (KubernetesResource resource : kubernetesResources) {
            // 设置镜像
            if (resource instanceof Deployment) {
                ((Deployment) resource).getSpec().getTemplate().getSpec().getContainers().get(0).setImage(newImageName);
                deploymentNames.add(((Deployment) resource).getMetadata().getName());
                // 对大数据单独处理
                // 大数据环境变量需要单独设置
                // 具体参考大数据应用的yaml
                processWithBigData((Deployment) resource, newImageName, nameSpace);
            }
            if (resource instanceof io.fabric8.kubernetes.api.model.Service) {
                serviceNames.add(((io.fabric8.kubernetes.api.model.Service) resource).getMetadata().getName());
            }


            // 设置命名空间
            setNameSpace(resource, nameSpace);
        }
        return kubernetesResources;
    }

    private void processWithBigData(Deployment deployment, String newImageName, String newNamespace) {
        List<EnvVar> envVars = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv();
        for (EnvVar envVar : envVars) {
            // flink && spark
            if ("JOB_NAMESPACE".equals(envVar.getName())) {
                envVar.setValue(newNamespace);
            }
            // flink
            if ("IMAGE_NAME".equals(envVar.getName())) {
                envVar.setValue(newImageName);
            }
            // spark
            if ("SPARK_CONTAINER_IMAGE".equals(envVar.getName())) {
                envVar.setValue(newImageName);
            }
        }
    }

    private void setNameSpace(KubernetesResource resource, String nameSpace) {
        // deployment service secret configmap serviceAccount role roleBinding
        if (resource instanceof Deployment) {
            ((Deployment) resource).getMetadata().setNamespace(nameSpace);
        }
        // service
        if (resource instanceof io.fabric8.kubernetes.api.model.Service) {
            ((io.fabric8.kubernetes.api.model.Service) resource).getMetadata().setNamespace(nameSpace);
        }
        // secret
        if (resource instanceof Secret) {
            ((Secret) resource).getMetadata().setNamespace(nameSpace);
        }
        // configmap
        if (resource instanceof ConfigMap) {
            ((ConfigMap) resource).getMetadata().setNamespace(nameSpace);
        }
        // serviceAccount
        if (resource instanceof ServiceAccount) {
            ((ServiceAccount) resource).getMetadata().setNamespace(nameSpace);
        }
        // GenericKubernetesResource
        if (resource instanceof GenericKubernetesResource) {
            ((GenericKubernetesResource) resource).getMetadata().setNamespace(nameSpace);
        }
        // role
        if (resource instanceof Role) {
            ((Role) resource).getMetadata().setNamespace(nameSpace);
        }
        // roleBinding
        if (resource instanceof RoleBinding) {
            ((RoleBinding) resource).getMetadata().setNamespace(nameSpace);
        }
    }

    /**
     * 获取带有前缀的路径，并合成一个string
     *
     * @param pathList      路径列表
     * @param applicationId 应用id
     * @return 结果
     */
    private String getPathWithPrefix(List<String> pathList, String applicationId, String svcId) {
        ApplicationPO applicationPO = applicationService.selectByAppId(applicationId);
        // 使用base64(userName)+category+timestamp作为路径前缀
        // 添加服务id，用于网关收集日志使用
        String prefix =
                Base64Encoder.encode(applicationPO.getUserName()) + "/" + svcId + "/" + applicationPO.getCategory() +
                        "/" + System.currentTimeMillis();
        return pathList.stream().map(path -> prefix + path).collect(Collectors.joining(","));
    }

    private ConfigInfo getConfigFromFile(Properties properties) {
        ConfigInfo configInfo = new ConfigInfo();
        // imageInfo
        ConfigImageInfo configImageInfo = new ConfigImageInfo();
        configImageInfo.setImage(properties.getProperty("image"));
        configImageInfo.setImageMd5(properties.getProperty("imageMd5"));
        configImageInfo.setImageName(properties.getProperty("imageName"));
        // serviceInfo
        ConfigServiceInfo configServiceInfo = new ConfigServiceInfo();
        configServiceInfo.setPath(Arrays.asList(properties.getProperty("path").split(",")));
        configServiceInfo.setFramework(properties.getProperty("framework"));
        configServiceInfo.setServiceName(properties.getProperty("serviceName"));
        // yamlInfo
        ConfigYamlInfo configYamlInfo = new ConfigYamlInfo();
        configYamlInfo.setYamlName(properties.getProperty("yaml"));

        configInfo.setConfigImageInfo(configImageInfo);
        configInfo.setConfigServiceInfo(configServiceInfo);
        configInfo.setConfigYamlInfo(configYamlInfo);
        return configInfo;
    }

    /**
     * 分页查询
     *
     * @param queryMicroServiceParam 参数
     * @return 结果
     */
    @Override
    public PageResult<MicroServiceVO> findByPage(QueryMicroServiceParam queryMicroServiceParam) {
        PageMethod.startPage(queryMicroServiceParam.getPageNum(), queryMicroServiceParam.getPageSize());
        List<MicroServicePO> microServicePOS;
        // 普通用户只能查询自己的
        microServicePOS = microServiceMapper.selectDeployedMicroServiceByParamWithUser(queryMicroServiceParam,
                TokenUtils.getUserName());
        List<MicroServiceVO> vos = new ArrayList<>(microServicePOS.size());
        for (MicroServicePO po : microServicePOS) {
            // 没有策略的不需要展示
            StrategyPO scheduleStrategy = strategyMapper.selectStrategyByServiceIdAndType(po.getId(),
                    StrategyTypeEnum.SCHEDULE_STRATEGY.getValue());
            List<StrategyPO> scaleStrategys = strategyMapper.selectScaleStrategysByServiceId(po.getId());
            if (Objects.isNull(scheduleStrategy) || Objects.isNull(scaleStrategys)) {
                LOGGER.warn("this micro service has no strategy, serviceName={}, applicationName={}",
                        po.getMicroServiceName(), po.getApplicationName());
                continue;
            }
            MicroServiceDTO dto = new MicroServiceDTO(po, scheduleStrategy, scaleStrategys);
            MicroServiceVO vo = dto.toVO();
            setExtraInfo(po, vo);

            // 拼接路径参数
            List<String> pathWithGateway =
                    vo.getPaths().stream().map(path -> gatewayConfig.getUrl() + "/" + path).collect(Collectors.toList());
            vo.setPaths(pathWithGateway);
            vos.add(vo);
        }
        PageInfo<MicroServicePO> realPageInfo = new PageInfo<>(microServicePOS);
        PageInfo<MicroServiceVO> pageInfoToShow = new PageInfo<>(vos);
        PageUtils.setRealPageInfo(pageInfoToShow, realPageInfo);
        return PageUtils.getPageResult(pageInfoToShow);
    }

    /**
     * 根据应用id删除啊微服务信息和策略信息
     *
     * @param applicationId 应用id
     */
    @Override
    @Transactional
    public void deleteMicroServiceAndStrategyWithAppId(String applicationId) {
        List<MicroServicePO> toDeleteSvcList = microServiceMapper.selectMicroServiceByAppId(applicationId);
        // 删除策略
        StrategyService strategyService = SpringUtil.getBean(StrategyService.class);
        for (MicroServicePO microServicePO : toDeleteSvcList) {
            strategyService.deleteStrategyBySvcId(microServicePO.getId());
            // 删除微服务智能调度历史记录
            strategyService.deleteIntelligentInfoBySvcId(microServicePO.getId());
        }
        // 删除微服务
        microServiceMapper.deleteMicroServiceByAppId(applicationId);
        // 删除指标
        indicatorService.deleteIndicatorCaseByAppId(applicationId);
    }

    /**
     * 根据服务id获取yaml
     *
     * @param serviceId serviceId
     * @return yaml
     */
    @Override
    public String getYamlByServiceId(String serviceId) {
        MicroServicePO microServicePO = microServiceMapper.selectMicroServiceById(serviceId);
        if (Objects.isNull(microServicePO)) {
            LOGGER.error("can't find micro service by id={}", serviceId);
            throw new JsonException(500, "未找到该微服务信息");
        }
        // 判断权限
        if (!microServicePO.getUserName().equals(TokenUtils.getUserName())) {
            LOGGER.error("user {} has no permissions to get microservice {} yaml", TokenUtils.getUserName(), serviceId);
            throw new JsonException(ResultCode.FORBIDDEN);
        }

        return microServicePO.getYaml();
    }

    /**
     * 验证
     *
     * @param validateParam 参数
     * @return 结果
     */
    @Override
    public boolean validate(ValidateParam validateParam) {
        MicroServicePO microServicePO = microServiceMapper.selectMicroServiceById(validateParam.getServiceId());
        if (Objects.isNull(microServicePO)) {
            LOGGER.error("can't find micro service by id={}", validateParam.getServiceId());
            throw new JsonException(500, "未找到该微服务信息");
        }
        // 判断权限
        //        if (!microServicePO.getUserName().equals(TokenUtils.getUserName())) {
        //            LOGGER.error("user {} has no permissions to validate microservice {}", TokenUtils.getUserName(),
        //                    validateParam.getServiceId());
        //            throw new JsonException(ResultCode.FORBIDDEN);
        //        }

        DeployPolicyValidateReq req = new DeployPolicyValidateReq();
        req.setServiceId(validateParam.getServiceId());

        // 使用数据库中的yaml
        // 当前页面不支持修改yaml
        req.setYaml(microServicePO.getYaml());
        if (CategoryEnum.CATEGORY_BIG_DATA.getValue().equals(microServicePO.getCategory())) {
            String newYaml = getNewYamlWithBigData(microServicePO);
            req.setYaml(newYaml);
        }
        req.setClusterNames(validateParam.getClusters());
        req.setAppCategory(microServicePO.getCategory());
        // 发送算法引擎请求
        CommentResultForEngine<ValidateResponse> result = algorithmEngineService.validate(req);
        List<ValidateResult> validateResults = result.getData().getResults();

        for (ValidateResult validateResult : validateResults) {
            if (!validateResult.isPass()) {
                throw new JsonException(500, validateResult.getDetailMessage());
            }
        }

        return true;
    }

    /**
     * 根据应用id查询微服务信息
     *
     * @param applicationId 应用id
     * @return 结果
     */
    @Override
    public List<MicroServiceVO> selectMicroServicesByAppId(String applicationId) {
        List<MicroServicePO> microServicePOS = microServiceMapper.selectMicroServiceByAppId(applicationId);
        List<MicroServiceVO> vos = new ArrayList<>(microServicePOS.size());
        for (MicroServicePO po : microServicePOS) {
            // 判断权限
            if (!po.getUserName().equals(TokenUtils.getUserName())) {
                LOGGER.error("user {} has no permissions for application {}", TokenUtils.getUserName(),
                        po.getApplicationId());
                throw new JsonException(ResultCode.FORBIDDEN);
            }

            StrategyPO scheduleStrategy = strategyMapper.selectStrategyByServiceIdAndType(po.getId(),
                    StrategyTypeEnum.SCHEDULE_STRATEGY.getValue());
            List<StrategyPO> scaleStrategys = strategyMapper.selectScaleStrategysByServiceId(po.getId());
            // 此处允许策略为空，有可能还未拆解，也可能策略被删除
            MicroServiceDTO dto = new MicroServiceDTO(po, scheduleStrategy, scaleStrategys);
            // 转换vo
            MicroServiceVO vo = dto.toVO();

            setExtraInfo(po, vo);
            vos.add(vo);
        }

        return vos;
    }

    private void setExtraInfo(MicroServicePO po, MicroServiceVO vo) {
        ClusterInfoPO clusterInfoPO = clusterInfoService.getClusterInfoBySvcId(po.getId());
        if (Objects.isNull(clusterInfoPO)) {
            return;
        }
        List<EngineClusterInfo> engineClusterInfos = clusterInfoPO.getClusterInfos();
        List<ClusterInfoVO> clusterInfoVOS = new ArrayList<>();
        // 按照云商分组
        Map<String, List<EngineClusterInfo>> tmpMap =
                engineClusterInfos.stream().collect(Collectors.groupingBy(EngineClusterInfo::getProviderName));
        // 循环过滤
        for (Map.Entry<String, List<EngineClusterInfo>> entry : tmpMap.entrySet()) {
            if (!clusterInfoPO.getUserChooseCloud().equals(entry.getKey())) {
                continue;
            }
            ClusterInfoVO clusterInfoVO = new ClusterInfoVO();
            clusterInfoVO.setManufacturer(entry.getKey());
            // 过滤非用户选择的云商
            List<String> clusterNames = entry.getValue().stream().map(EngineClusterInfo::getName).collect(Collectors.toList());
            clusterInfoVO.setClusterNames(clusterNames);
            clusterInfoVOS.add(clusterInfoVO);
        }
        vo.setClusterInfos(clusterInfoVOS);
        vo.setAlgDeployClusterInfos(clusterInfoPO.getAlgDeployClusterInfos());

        vo.setUserChooseCloud(clusterInfoPO.getUserChooseCloud());
        vo.setRecommendCloud(clusterInfoPO.getRecommendCloud());
        vo.setForcedIsolation(clusterInfoPO.isForcedIsolation());
    }

    /**
     * 部署微服务
     *
     * @param distributeParam 参数
     * @return 结果
     */
    @Override
    public boolean distribute(DistributeParam distributeParam) {
        MicroServicePO microServicePO = microServiceMapper.selectMicroServiceById(distributeParam.getServiceId());
        if (Objects.isNull(microServicePO)) {
            LOGGER.error("can't find micro service, id={}", distributeParam.getServiceId());
            throw new JsonException(500, "未找到该微服务");
        }
        // 判断权限
        //        if (!microServicePO.getUserName().equals(TokenUtils.getUserName())) {
        //            LOGGER.error("user {} has no permissions to distribute microservice {}", TokenUtils.getUserName(),
        //                    distributeParam.getServiceId());
        //            throw new JsonException(ResultCode.FORBIDDEN);
        //        }

        StrategyPO scheduleStrategy = strategyMapper.selectStrategyByServiceIdAndType(distributeParam.getServiceId(),
                StrategyTypeEnum.SCHEDULE_STRATEGY.getValue());
        List<StrategyPO> scaleStrategys =
                strategyMapper.selectScaleStrategysByServiceId(distributeParam.getServiceId());
        if (Objects.isNull(scheduleStrategy) || Objects.isNull(scaleStrategys)) {
            LOGGER.error("micro service id={} has no strategy", distributeParam.getServiceId());
            throw new JsonException(500, "请配置策略");
        }
        // 1. 过滤虚机扩缩容的策略
        // 2.1 需要确认集群扩缩容为空时，发送什么参数
        // 2.2 应该去除none的参数
        scaleStrategys =
                scaleStrategys.stream().filter(po -> !"虚机扩缩容策略".equals(po.getStrategyName())).filter(po -> !(
                        "{\"policy\":\"none\"}").equals(po.getStrategyParam())).collect(Collectors.toList());
        // 组装请求
        DeployPolicyDistributeReq req = new DeployPolicyDistributeReq();
        req.setSvcId(distributeParam.getServiceId());
        req.setYaml(microServicePO.getYaml());
        req.setPath(microServicePO.getPath());
        req.setClusters(distributeParam.getClusters());
        req.setDeployPolicyConfig(scheduleStrategy.getStrategyParam());


        req.setScalePolicyConfigs(scaleStrategys.stream().map(StrategyPO::getStrategyParam).collect(Collectors.toList()));
        req.setAppCategory(microServicePO.getCategory());
        req.setFramework(microServicePO.getFramework());
        req.setUserName(microServicePO.getUserName());

        // 大数据单独处理
        if (CategoryEnum.CATEGORY_BIG_DATA.getValue().equals(microServicePO.getCategory())) {
            String newYaml = getNewYamlWithBigData(microServicePO);
            req.setYaml(newYaml);
        }
        // 需要获取应用的信息
        ApplicationPO applicationPO = applicationService.selectByAppId(microServicePO.getApplicationId());
        ApplicationLabel labels = applicationPO.getLabel();
        req.setSceneTags(labels.getObjectScene());
        req.setTechnologyTags(labels.getTechnology());

        // 更新微服务状态
        //microServiceMapper.updateMicroServiceStatus(distributeParam.getServiceId(),
        //StrategyStatusEnum.STARTING.getStatus());

        // 发送请求
        CommentResultForEngine<ValidateResponse> result = algorithmEngineService.distribute(req);
        List<ValidateResult> validateResults = result.getData().getResults();
        for (ValidateResult validateResult : validateResults) {
            if (!validateResult.isPass()) {
                throw new JsonException(500, validateResult.getDetailMessage());
            }
        }

        // 部署成功后，需要更新旧部署信息表
        ClusterInfoPO clusterInfoPO = clusterInfoService.getClusterInfoBySvcId(distributeParam.getServiceId());
        List<EngineClusterInfo> currentEngine = scheduleStrategy.getClusterInfos();
        for (EngineClusterInfo engineClusterInfo : currentEngine) {
            engineClusterInfo.setActive(engineClusterInfo.getProviderName().equals(clusterInfoPO.getUserChooseCloud()));
        }
        scheduleStrategy.setOldClusterInfos(currentEngine);
        strategyMapper.updateStrategy(scheduleStrategy);
        LOGGER.info("update current engine info, {}", JsonUtil.objectToJsonStr(scheduleStrategy));
        return true;
    }

    private String getNewYamlWithBigData(MicroServicePO microServicePO) {
        ClusterInfoPO clusterInfoPO = clusterInfoService.getClusterInfoBySvcId(microServicePO.getId());
        List<EngineClusterInfo> clusterInfos = clusterInfoPO.getClusterInfos();
        String ip = clusterInfos.get(0).getInnerIp();
        String k8sMasterIp = "k8s://https://" + ip + ":6443";

        String[] strings = microServicePO.getYaml().split("---");
        List<KubernetesResource> newResources = new ArrayList<>();
        for (String s : strings) {
            KubernetesResource resource = Serialization.unmarshal(s);
            if (resource instanceof Deployment) {
                Deployment deployment = (Deployment) resource;
                List<EnvVar> envVars = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv();
                for (EnvVar envVar : envVars) {
                    if ("SPARK_MASTER".equals(envVar.getName())) {
                        envVar.setValue(k8sMasterIp);
                        break;
                    }
                }
            }
            newResources.add(resource);
        }
        return newResources.stream().map(Serialization::asYaml).collect(Collectors.joining());
    }

    /**
     * 更新微服务状态
     *
     * @param param 参数
     * @return 结果
     */
    @Override
    public boolean updateMicroServiceStatus(UpdateMicroServiceStatusParam param) {
        MicroServicePO microServicePO = microServiceMapper.selectMicroServiceById(param.getId());
        if (Objects.isNull(microServicePO)) {
            LOGGER.error("can't find micro service id={}", param.getId());
            throw new JsonException(404, "未找到该微服务信息");
        }
        // 彻底删除时，需要删除本地微服务信息
        if (StrategyStatusEnum.DELETED.getStatus() == Integer.parseInt(param.getStatus())) {
            // 删除微服务后需要通知集群管理系统，删除本地镜像

            List<DeployClusterPO> deployClusterPOS = clusterInfoService.getDeployClusterBySvcId(param.getId());

            if (!deployClusterPOS.isEmpty()) {
                LOGGER.info("start send request to multi system to delete images, svcId={}", param.getId());
                DeleteLocalImageParam deleteLocalImageParam = new DeleteLocalImageParam();
                deleteLocalImageParam.setImageName(microServicePO.getImageName());
                deleteLocalImageParam.setSvcId(param.getId());
                deleteLocalImageParam.setK8sServiceName(microServicePO.getServiceName());
                List<EngineClusterInfo> engineClusterInfos = deployClusterPOS.stream()
                        .map(DeployClusterPO::toEngineClusterInfo)
                        .filter(Objects::nonNull)
                        .collect(Collectors.toList());
                deleteLocalImageParam.setClusterInfos(engineClusterInfos);
                CommentResultForCloud cloudResult = cloudMgmtService.deleteLocalImageByServiceInfo(deleteLocalImageParam);
                if (cloudResult.getCode() != 200) {
                    LOGGER.warn("can't delete local image, svcId={}, old clusterInfos={}", param.getId(), JsonUtil.objectToJsonStr(deployClusterPOS));
                }
            }

            // 删除集群信息
            clusterInfoService.deleteClusterInfoBySvcId(param.getId());
            // 删除策略

            StrategyService strategyService = SpringUtil.getBean(StrategyService.class);
            strategyService.deleteStrategyBySvcId(param.getId());
            strategyService.deleteIntelligentInfoBySvcId(param.getId());

            // 微服务更新为初始状态
            int s = microServiceMapper.updateMicroServiceStatus(param.getId(), StrategyStatusEnum.INIT.getStatus());
            // 更新应用状态
            ApplicationService applicationService = SpringUtil.getBean(ApplicationService.class);
            if (checkAllServiceHasBeenDeleted(microServicePO.getApplicationId())) {
                applicationService.updateApplicationStatus(microServicePO.getApplicationId(), ApplicationStatusEnum.SPLIT.getValue());
            } else {
                applicationService.updateApplicationStatus(microServicePO.getApplicationId(), ApplicationStatusEnum.IN_DISTRIBUTE.getValue());

            }
            // 删除该微服务阶段信息
            DistributeDeployService distributeDeployService = SpringUtil.getBean(DistributeDeployService.class);
            distributeDeployService.deleteStagesByServiceId(param.getId());

            // 删除20分钟的延时任务
            ScheduledFuture<?> future = ASYNC_DELETE_MAP.remove(param.getId());
            if (Objects.nonNull(future)) {
                future.cancel(true);
                LOGGER.warn("[delete-svc]: stop async thread by delete successfully, svcId={}", param.getId());
            }

            return s == 1;
        }
        if (StrategyStatusEnum.FAILED.getStatus() == Integer.parseInt(param.getStatus())) {
            LOGGER.error("microservice(svcId={}) has failed, update application to split", param.getId());
            // 更新应用状态
            applicationService.updateApplicationStatus(microServicePO.getApplicationId(), ApplicationStatusEnum.DISTRIBUTE_FAILURE.getValue());
        }
        // 如果是已运行或者已结束，需要更新阶段信息
        int status = Integer.parseInt(param.getStatus());
        if (StrategyStatusEnum.APPLIED.getStatus() == status || StrategyStatusEnum.FINISHED.getStatus() == status) {
            ApplicationPO applicationPO = applicationService.selectByAppId(microServicePO.getApplicationId());
            DeployResourceInfo deployResourceInfo = applicationPO.getDeployResourceInfo();

            ScheduleStrategyPO scheduleStrategyPO = scheduleStrategyMapper.selectByServiceId(microServicePO.getId());
            String arch = scheduleStrategyPO.checkArch();

            LOGGER.info("update microservice status to {}, update stage info with distribute", status);
            // 当算法引擎更新微服务状态为已启动时，更新阶段4的二级步骤
            DistributeDeployService distributeDeployService = SpringUtil.getBean(DistributeDeployService.class);
            List<StageEnum2> distributeStages = StageEnum2.getDistributeStageEnum2ByCloudTypeAndArch(deployResourceInfo.getCloudScope(), arch);
            for (StageEnum2 stage : distributeStages) {
                UpdateStageParam stageParam = new UpdateStageParam();
                stageParam.setSvcId(param.getId());
                stageParam.setType(stage.getParentType());
                stageParam.setDetailType(stage.getDetailType());
                stageParam.setOrder(stage.getOrder());
                stageParam.setCloudScope(deployResourceInfo.getCloudScope());
                stageParam.setArch(arch);
                distributeDeployService.updateStage(stageParam);
            }
            // 发送虚机扩缩容策略到集群控制台
            ScaleStrategyPO vmPo = scaleStrategyMapper.selectByServiceIdAndScaleType(param.getId(), ScaleStrategyTypeEnum.VM_SCALE_STRATEGY.getType());

            if (Objects.nonNull(vmPo)) {
                List<DeployClusterPO> clusterPOS = clusterInfoService.getDeployClusterBySvcId(param.getId());
                List<CreateScalingDTO> createScalingDTOS = clusterPOS.stream().map(po -> {
                    CreateScalingDTO createScalingDTO = new CreateScalingDTO();
                    createScalingDTO.setClusterNames(po.getClusterName());
                    createScalingDTO.setProvider(CloudVendorsEnum.changeFromCloudTypeToDescribe(po.getProviderName()));
                    createScalingDTO.setScalingLabel(param.getId());
                    createScalingDTO.setAccountName(po.getAccountName());

                    // TODO 此处默认所有集群都使用一个虚机扩缩容配置
                    ScalingPolicyDTO scalingPolicyDTO = JsonUtil.jsonStrToObject(vmPo.getStrategyParam(), ScalingPolicyDTO.class);
                    if (Objects.nonNull(scalingPolicyDTO) && !"none".equals(scalingPolicyDTO.getPolicy())) {
                        createScalingDTO.setScalingPolicyPOs(scalingPolicyDTO.getParam());
                    } else {
                        createScalingDTO.setScalingPolicyPOs(null);
                    }
                    return createScalingDTO;
                }).collect(Collectors.toList());

                CommentResultForCloud result = cloudMgmtService.insertNewScalingPolicy(createScalingDTOS);
                LOGGER.info("update scaling policy for svc={}, policy={}", param.getId(), JsonUtil.objectToJsonStr(createScalingDTOS));
            }
            applicationService.updateApplicationStatus(microServicePO.getApplicationId(), ApplicationStatusEnum.IN_DISTRIBUTE.getValue());
        }
        int result = microServiceMapper.updateMicroServiceStatus(param.getId(), Integer.parseInt(param.getStatus()));
        if (result != 1) {
            LOGGER.error("update micro service status fail, id={}, status={}", param.getId(), param.getStatus());
            return false;
        }

        // 部署一个微服务之后，需要判断当前应用是否已经全部部署完成，如果全部部署完成，则应用变为已部署
        List<MicroServicePO> microServicePOS = microServiceMapper.selectMicroServiceByAppId(microServicePO.getApplicationId());
        boolean allServiceHasBeenDeployed = true;
        for (MicroServicePO po : microServicePOS) {
            if (StrategyStatusEnum.APPLIED.getStatus() != po.getStatus() && StrategyStatusEnum.FINISHED.getStatus() != po.getStatus()) {
                allServiceHasBeenDeployed = false;
                break;
            }
        }

        if (allServiceHasBeenDeployed) {
            // 更新应用状态
            applicationService.updateApplicationStatus(microServicePO.getApplicationId(), ApplicationStatusEnum.DISTRIBUTE.getValue());
        }
        if (checkAllServiceHasBeenDeleted(microServicePO.getApplicationId())) {
            applicationService.updateApplicationStatus(microServicePO.getApplicationId(), ApplicationStatusEnum.SPLIT.getValue());
        }

        return true;
    }
    private boolean checkAllServiceHasBeenDeleted(String appId) {
        boolean allServiceHasBeenDeleted = true;
        List<MicroServicePO> microServicePOS = microServiceMapper.selectMicroServiceByAppId(appId);
        for (MicroServicePO po : microServicePOS) {
            if (StrategyStatusEnum.INIT.getStatus() != po.getStatus()) {
                allServiceHasBeenDeleted = false;
                break;
            }
        }
        return allServiceHasBeenDeleted;
    }
    /**
     * 根据应用id删除微服务镜像
     *
     * @param applicationId 应用id
     * @return 结构
     */
    @Override
    public boolean deleteMicroServiceImagesWithAppId(String applicationId) {
        List<MicroServicePO> microServicePOS = microServiceMapper.selectMicroServiceByAppId(applicationId);

        if (microServicePOS.isEmpty()) {
            LOGGER.warn("app={} has no microservices in db, just return true", applicationId);
            return true;
        }
        // 发送harbor请求时，只能成功第一个请求，所以这里单独处理
        // 猜测一个线程只有第一请求会被harbor认证
        // 使用多线程分别发送请求
        AtomicBoolean atomicBoolean = new AtomicBoolean(true);
        List<CompletableFuture<Void>> futures = new ArrayList<>();
        for (MicroServicePO po : microServicePOS) {
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                boolean result = deleteImage(po.getImageName());
                atomicBoolean.set(result && atomicBoolean.get());
            });
            futures.add(future);
        }
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
        if (!atomicBoolean.get()) {
            LOGGER.error("delete all images fail");
            return false;
        }
        return true;
    }

    /**
     * 删除镜像
     *
     * @param imageFullName 镜像全称
     * @return
     */
    private boolean deleteImage(String imageFullName) {
        // 获取删除镜像的url
        String deleteUrl = dockerConfig.getDeleteImageUrl(imageFullName);

        HttpResponse response =
                HttpRequest.delete(deleteUrl).basicAuth(dockerConfig.getRegistryUserName(), dockerConfig.getRegistryPassword()).execute();
        if (!response.isOk()) {
            // 如果是镜像未找到，则也认为删除成功
            if (HttpStatus.HTTP_NOT_FOUND == response.getStatus()) {
                LOGGER.warn("delete image {} success with image not found", imageFullName);
                return true;
            }
            String res = response.body();
            LOGGER.error("delete image {} fail, response from harbor: {}", imageFullName, res);
            return false;
        }
        return true;
    }

    /**
     * 更新部署集群信息
     *
     * @param param 参数
     * @return 结果
     */
    @Override
    @Transactional
    public boolean updateClusterInfo(UpdateMicroserviceClusterParam param) {
        StrategyPO scheduleStrategy = strategyMapper.selectStrategyByServiceIdAndType(param.getSvcId(),
                StrategyTypeEnum.SCHEDULE_STRATEGY.getValue());
        StrategyPO scaleStrategy = strategyMapper.selectStrategyByServiceIdAndType(param.getSvcId(),
                StrategyTypeEnum.SCALE_STRATEGY.getValue());

        if (Objects.isNull(scheduleStrategy) || Objects.isNull(scaleStrategy)) {
            LOGGER.error("micro service has empty strategy, id={}", param.getSvcId());
            throw new JsonException(400, "参数错误，未找到微服务部署信息");
        }

        // 单独更新cluster_info表
        MicroServicePO microServicePO = microServiceMapper.selectMicroServiceById(param.getSvcId());
        clusterInfoService.saveOrUpdateClusterInfo(microServicePO, param.getClusterInfos());
        return true;
    }

    /**
     * 根据服务id查询微服务信息
     *
     * @param serviceId 服务id
     * @return 结果
     */
    @Override
    public MicroServicePO getMicroserviceBySvcId(String serviceId) {
        return microServiceMapper.selectMicroServiceById(serviceId);
    }

    /**
     * 更新用户选择
     *
     * @param param 参数
     */
    @Override
    public void updateUserChooseCloud(UpdateUserChooseParam param) {
        MicroServicePO microServicePO = microServiceMapper.selectMicroServiceById(param.getSvcId());
        if (Objects.isNull(microServicePO)) {
            LOGGER.error("can't find service, svcId={}", param.getSvcId());
            throw new JsonException(404, "该微服务不存在");
        }
        // 获取调度策略
        StrategyPO scheduleStrategy = strategyMapper.selectStrategyByServiceIdAndType(param.getSvcId(),
                StrategyTypeEnum.SCHEDULE_STRATEGY.getValue());
        if (Objects.isNull(scheduleStrategy)) {
            LOGGER.error("this service has no schedule strategy, svcId={}", param.getSvcId());
            throw new JsonException(404, "该微服务没有配置调度策略");
        }
        // 更新
        clusterInfoService.updateUserChooseCloud(param);
    }

    @Override
    public void updateScalingPolicy(UpdateScalingParam updateScalingParam) {
        List<ClusterInfoPO> allInfos = clusterInfoService.getClusterInfoByUserChooseCloud(updateScalingParam.getProvider());
        for (ClusterInfoPO clusterInfoPO : allInfos) {
            List<EngineClusterInfo> chooseCloudEngineInfos = clusterInfoPO.getClusterInfos().stream()
                    .filter(info -> updateScalingParam.getClusterName().equals(info.getName()))
                    .collect(Collectors.toList());
            if (chooseCloudEngineInfos.isEmpty()) {
                continue;
            }
            StrategyPO vmStrategy = strategyMapper.selectVmScaleByServiceId(clusterInfoPO.getId());
            if (Objects.isNull(vmStrategy)) {
                // 为空则不需要处理
                continue;
            }

            MicroServicePO microServicePO = microServiceMapper.selectMicroServiceById(clusterInfoPO.getId());
            if (!microServicePO.getCategory().equals(updateScalingParam.getCategory())) {
                LOGGER.info("current service={} is {}, update category is {}, just return", clusterInfoPO.getId(),
                        microServicePO.getCategory(), updateScalingParam.getCategory());
                continue;
            }

            List<ScalingPolicyPO> scalingPolicyPOS = updateScalingParam.getScalingPolicyPOS();
            if (Objects.isNull(scalingPolicyPOS) || scalingPolicyPOS.isEmpty()) {
                // 为空，则更新策略为不配置
                vmStrategy.setStrategyParam("{\"policy\":\"none\"}");
                vmStrategy.setStrategyName("不配置");
            } else {
                ScalingPolicyDTO scalingPolicyDTO = new ScalingPolicyDTO();
                scalingPolicyDTO.setPolicy("vm");
                scalingPolicyDTO.setParam(scalingPolicyPOS);

                vmStrategy.setStrategyName("虚机扩缩容策略");
                vmStrategy.setStrategyParam(JsonUtil.objectToJsonStr(scalingPolicyDTO));

            }
            strategyMapper.updateStrategy(vmStrategy);
        }
    }
}
